package bancosys.tec.security.impl.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Comments;
import jmine.tec.persist.annotation.Index;
import jmine.tec.persist.annotation.Indexes;
import jmine.tec.persist.annotation.UniqueConstraint;
import jmine.tec.persist.schema.api.IndexOrder;

import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ForeignKey;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.authorization.annotation.DisplayName;
import bancosys.tec.persist.bussobj.AuthPersistableBusinessObject;
import bancosys.tec.persist.persister.annotation.NaturalKey;

/**
 * Grupo de usuários.
 * 
 * @author Marco
 */
@Entity
@Table(name = "USER_GROUP")
@Alias("USRGRO")
@SequenceGenerator(name = "SEQ_USRGRO", sequenceName = "SEQ_USRGRO")
@Filters({ @Filter(name = "authFilter", condition = "auth = :auth") })
@NamedQueries({
// recuperação de usuários com Group
@NamedQuery(name = "grupo.entityIds", query = "select chineseId.entityId " + "from User usuario join usuario.groups grupo "
        + "join grupo.credentials credential join " + "credential.ids chineseId where usuario.id = ? "
        + "and credential.class = ChineseWallCredential and " + "credential.entityName = ? and " + "credential.client.id = ?") })
@DisplayName("Grupo")
@Documentation("ARMAZENA OS GRUPOS DE USUARIOS PARA ACESSO AO SISTEMA.")
@UniqueConstraint(columns = { "AUTH", "NAME_USER_GROUP" }, name = "AK_USRGRO_1")
public class Group extends AuthPersistableBusinessObject {

    /**
     * ID do grupo.
     */
    private Long id;

    /**
     * Nome do grupo.
     */
    private String name;

    /**
     * Credencias associadas ao grupo.
     */
    private Set<Credential> credentials;

    /**
     * Construtor.
     */
    Group() {
        // não faz nada
    }

    /**
     * Constrói um grupo, definindo seu nome.
     * 
     * @param name nome do grupo.
     */
    public Group(String name) {
        this.name = name;
    }

    /**
     * Retorna o ID do grupo.
     * 
     * @return ID do grupo.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_USRGRO")
    @Column(name = "COD_USER_GROUP", nullable = false)
    @Documentation("CODIGO DO GRUPO DE USUARIOS.")
    public Long getId() {
        return this.id;
    }

    /**
     * Define o ID do grupo.
     * 
     * @param id ID do grupo.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * Retorna o nome do grupo.
     * 
     * @return nome do grupo.
     */
    @Column(name = "NAME_USER_GROUP", nullable = false, length = 100)
    @DisplayName(value = "Grupo", order = "10")
    @Documentation("NOME DO GRUPO DE USUARIOS.")
    @NaturalKey
    public String getName() {
        return this.name;
    }

    /**
     * Define o nome do grupo.
     * 
     * @param name nome do grupo.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Retorna o conjunto de credenciais associados ao grupo.
     * 
     * @return conjunto de credenciais associados ao grupo.
     */
    @ManyToMany(targetEntity = Credential.class, fetch = FetchType.EAGER)
    @JoinTable(name = "ASSOC_USER_GROUP_CREDENTIAL", joinColumns = { @JoinColumn(name = "COD_USER_GROUP") }, inverseJoinColumns = { @JoinColumn(name = "COD_"
            + "CREDENTIAL") })
    @ForeignKey(name = "FK_USRGRO_ASUSGC", inverseName = "FK_CREDEN_ASUSGC")
    @Alias("ASUSGC")
    @org.hibernate.annotations.Fetch(FetchMode.SELECT)
    @DisplayName(value = "Credenciais", order = "11")
    @Comments({ @Comment(table = "ASSOC_USER_GROUP_CREDENTIAL", value = "ARMAZENA A ASSOCIACAO ENTRE GRUPO DE USUARIOS E AS CREDENCIAIS."),
            @Comment(table = "ASSOC_USER_GROUP_CREDENTIAL", column = "COD_CREDENTIAL", value = "CODIGO DA CREDENCIAL."),
            @Comment(table = "ASSOC_USER_GROUP_CREDENTIAL", column = "COD_USER_GROUP", value = "CODIGO DO GRUPO DE USUARIO.") })
    @Indexes({ @Index(columns = "COD_USER_GROUP", suffix = "0", orders = IndexOrder.ASC),
            @Index(columns = "COD_CREDENTIAL", suffix = "1", orders = IndexOrder.ASC) })
    public Set<Credential> getCredentials() {
        if (this.credentials == null) {
            this.credentials = new HashSet<Credential>();
        }
        return this.credentials;
    }

    /**
     * Define as credenciais do grupo.
     * 
     * @param credentials credenciais do grupo.
     */
    public void setCredentials(Set<Credential> credentials) {
        this.credentials = credentials;
    }

    /**
     * Adiciona uma credencial a lista a que o grupo pertence.
     * 
     * @param credential a credencial a ser adicionada.
     */
    public void addCredential(Credential credential) {
        this.getCredentials().add(credential);
    }

    /**
     * Devolve a representação do grupo.
     * 
     * @return a representação do grupo.
     */
    @Override
    public String toString() {
        return "Grupo: " + this.name;
    }

    /**
     * Devolve o hashCode.
     * 
     * @return o hashCode.
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.credentials == null) ? 0 : this.credentials.hashCode());
        result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
        return result;
    }

    /**
     * Compara esse grupo com o passado como parâmetro. Utiliza nome da entidade e o conjunto de credenciais como critério de comparação.
     * 
     * @param obj o outro objeto.
     * @return <code>true</code> se forem iguais, <code>false</code> caso contrário.
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Group)) {
            return false;
        }

        final Group other = (Group) obj;
        if ((this.credentials == null && other.credentials != null) || !this.credentials.equals(other.credentials)) {
            return false;
        }

        if ((this.name == null && other.name != null) || !this.name.equals(other.name)) {
            return false;
        }

        return true;
    }
}
